package com.qf.sturedis.controller;

import com.alibaba.fastjson.JSON;
import com.qf.sturedis.pojo.R;
import com.qf.sturedis.pojo.Student;
import com.qf.sturedis.service.StudentService;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/stu")
public class StudentController3 implements InitializingBean {

    public static final String STU_CACHE_PREFIX = "stu:cache:id:";
    public static final String STU_LOCK_PREFIX = "stu:lock:id:";

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private StudentService studentService;

    private RBloomFilter<Object> stuBloom;

    //初始化阶段会判断这个对象是否是InitializingBean的类型，如果是，会自动执行他的afterPropertiesSet方法
    @Override
    public void afterPropertiesSet() throws Exception {
        stuBloom = redissonClient.getBloomFilter("stuBloom");
        /*
            参数一：预期向布隆过滤器中存入的元素个数
            参数二：误判的错误概率
         */
        stuBloom.tryInit(10000,0.1);

        //查询到数据库现有的id值
        List<Integer> ids = studentService.findIds();
        //存入到布隆过滤器中
        ids.forEach(id-> stuBloom.add(id) );
    }

    @GetMapping("/{id}")
    public R findOne(@PathVariable("id") Integer id) throws InterruptedException {

        Student stu = null;

        //判断当前查询的id是否在布隆过滤器内部
        if (stuBloom.contains(id)) {

            //引入缓存
            String key = STU_CACHE_PREFIX + id;
            //先查询缓存
            String valueJson = redisTemplate.opsForValue().get(key);
            //判断缓存是否存在
            if (!StringUtils.isEmpty(valueJson)) {
                //有缓存：返回数据
                System.out.println("缓存中存在....");
                stu = JSON.parseObject(valueJson, Student.class);
            } else {

                String lockName = STU_LOCK_PREFIX + id;
                RLock lock = redissonClient.getLock(lockName);

                try {
                    //加锁
                    lock.lock();

                    //二次缓存的校验
                    valueJson = redisTemplate.opsForValue().get(key);
                    if (!StringUtils.isEmpty(valueJson)){
                        //有缓存：返回数据
                        System.out.println("缓存中存在....");
                        stu = JSON.parseObject(valueJson, Student.class);
                    }else {
                        //没有缓存: 查数据库 + 存入缓存 + 返回数据
                        System.out.println("缓存中不存在，查询数据库....");
                        Thread.sleep(500);
                        stu = studentService.getById(id);
                        if (stu != null) {
                            valueJson = JSON.toJSONString(stu);
                            //设置缓存5s过期，模拟缓存击穿
                            redisTemplate.opsForValue().set(key, valueJson, 5, TimeUnit.SECONDS);
                        }
                    }
                }finally {
                    //释放锁
                    lock.unlock();
                }
            }

            //返回结果
            return R.success(stu);
        }else{
            //数据库不存在该id对应的数据
            return R.success("数据库无此数据");
        }
    }

}
